#include "stdafx.h"
#include "../client/DCPlusPlus.h"
#include "Resource.h"

#include "ScriptEditorFrame.h"
#include "WinUtil.h"
#include "../client/File.h"
#include "../client/SyntaxManager.h"
#include "LineDlg.h"

#define MAX_TEXT_LEN 32768
#define BUFLEN 256

HWND ScriptEditorFrame::createToolbar() {
	CToolBarCtrl ctrlToolbar;
	images.CreateFromImage(IDB_SCRIPT, 16, 5, CLR_DEFAULT, IMAGE_BITMAP, LR_CREATEDIBSECTION | LR_SHARED);

	ctrlToolbar.Create(m_hWnd, NULL, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS, 0, ATL_IDW_TOOLBAR);
	ctrlToolbar.SetImageList(images);

	const int numButtons = 6;

	TBBUTTON tb[numButtons];
	memset(tb, 0, sizeof(tb));
	int n = 0, bitmap = 0;

#define ADD_BUTTON(a) \
	tb[n].iBitmap = bitmap++; \
	tb[n].idCommand = a; \
	tb[n].fsState = TBSTATE_ENABLED; \
	tb[n].fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE; \
	n++;

#define ADD_SEPERATOR() \
	tb[n].fsStyle = TBSTYLE_SEP; \
	n++;

	ADD_BUTTON(IDC_NEW_SCRIPT)
	ADD_BUTTON(IDC_SAVE_SCRIPT)
	ADD_SEPERATOR()
	ADD_BUTTON(ID_EDIT_CUT)
	ADD_BUTTON(ID_EDIT_COPY)
	ADD_BUTTON(ID_EDIT_PASTE)

	ctrlToolbar.SetButtonStructSize();
	ctrlToolbar.AddButtons(numButtons, tb);
	ctrlToolbar.AutoSize();

	return ctrlToolbar.m_hWnd;
}
LRESULT ScriptEditorFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
	HWND hWndToolBar = createToolbar();
	CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
	AddSimpleReBarBand(hWndToolBar, NULL, TRUE);

	CreateSimpleStatusBar(ATL_IDS_IDLEMESSAGE, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP);
	ctrlStatus.Attach(m_hWndStatusBar);

	WinUtil::decodeFont(SETTING(SCRIPT_FONT), scriptFont);
	textColor = SETTING(SCRIPT_NORMAL_COLOUR);
	bgColor = SETTING(SCRIPT_BACKGROUND_COLOUR);
	bgBrush = CreateSolidBrush(bgColor);

	horSplit.Create(*this, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
	verSplit.Create(horSplit, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);

	ctrlDebug.Create(verSplit, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 
		WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_NOHIDESEL | ES_READONLY, WS_EX_CLIENTEDGE);
	ctrlDebug.LimitText(0);
	ctrlDebug.SetFont(::CreateFontIndirect(&scriptFont));

	//ctrlScripts.Attach(GetDlgItem(IDC_SCRIPT_LIST));
	ctrlScripts.Create(verSplit, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 
		WS_HSCROLL | WS_VSCROLL | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL, WS_EX_CLIENTEDGE, IDC_SCRIPT_LIST);

	ctrlScripts.SetExtendedListViewStyle(BOOLSETTING(FULL_ROW_SELECT) ? LVS_EX_FULLROWSELECT : 0 | LVS_EX_HEADERDRAGDROP);

	ctrlScripts.SetBkColor(bgColor);
	ctrlScripts.SetTextBkColor(bgColor);
	ctrlScripts.SetTextColor(textColor);

	ctrlScripts.InsertColumn(0, CSTRING(FILENAME), LVCFMT_LEFT, 0, 0);

	ctrlPad.Create(horSplit, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | 
		WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE /*| ES_NOHIDESEL*/ | ES_WANTRETURN, WS_EX_CLIENTEDGE, IDC_SCRIPT_PAD);
	padContainer.SubclassWindow(ctrlPad.m_hWnd);
	ctrlPad.LimitText(0);
	ctrlPad.SetBackgroundColor(bgColor);
	ctrlPad.SetFont(::CreateFontIndirect(&scriptFont));

	horSplit.SetSplitterPanes(ctrlPad, verSplit);
	verSplit.SetSplitterPanes(ctrlDebug, ctrlScripts);

	m_hWndClient = horSplit;

	verSplit.SetSplitterExtendedStyle(SPLIT_PROPORTIONAL);
	verSplit.m_nProportionalPos = 8000;
	horSplit.SetSplitterExtendedStyle(SPLIT_PROPORTIONAL);
	horSplit.m_nProportionalPos = 8000;


	string curText, target = SETTING(LAST_SCRIPT);
	if(!target.empty()) {
		string::size_type i = target.rfind('\\');
		if(i != string::npos ) {
			currentFileName = target.substr(i+1);
		}
		try {
			curText = File(target, File::READ, File::OPEN).read();
		} catch(const FileException&) {
			// ...
		}
	}

	WIN32_FIND_DATA data;
	HANDLE hFind;
	
	hFind = FindFirstFile((Util::getAppPath() + "scripts\\*").c_str(), &data);
	if(hFind != INVALID_HANDLE_VALUE) {
		do {
			tstring name = data.cFileName;
			if(name == "." || name == "..")
				continue;
			if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				// maybe we should look in directories...
			} else {
				// this must be a file.
				ctrlScripts.insert(ctrlScripts.GetItemCount(), name.c_str());
			}
		} while(FindNextFile(hFind, &data));
		FindClose(hFind);
	}

	ctrlPad.ClearKeywords();
	ctrlPad.ClearConstants();
	ctrlPad.ClearOperators();

	Keywords::List& kl = SyntaxManager::getInstance()->getKeywords();
	for(Keywords::Iter i = kl.begin(); i != kl.end(); ++i) {
		switch(i->getType()) {
			case _KEYWORD: ctrlPad.AddKeyword(i->getKeyword().c_str()); break;
			case _LIBRARY: ctrlPad.AddConstant(i->getKeyword().c_str()); break;
			case _OPERATOR: ctrlPad.AddOperator(i->getKeyword()[0]); break;
			default: dcassert(0);
		}
	}

	ctrlPad.SetSyntax(SC_COMMENT, "--");
	SYNTAXCOLOR ic = { 0 };
	ic.clrText = textColor;
	ctrlPad.SetSyntaxColor(SC_NORMAL, ic);
	ic.clrText = SETTING(LIBRARY_COLOUR);
	ctrlPad.SetSyntaxColor(SC_LIBRARY, ic);
	ic.clrText = SETTING(OPERATOR_COLOUR);
	ctrlPad.SetSyntaxColor(SC_OPERATOR, ic);
	ic.clrText = SETTING(KEYWORD_COLOUR);
	ctrlPad.SetSyntaxColor(SC_KEYWORD, ic);
	ic.clrText = SETTING(COMMENT_COLOUR);
	ctrlPad.SetSyntaxColor(SC_COMMENT, ic);
	ic.clrText = SETTING(QUOTE_COLOUR);
	ctrlPad.SetSyntaxColor(SC_STRING, ic);

	ctrlPad.SetWindowText(curText.c_str());
	ctrlPad.FormatAll();
	ctrlPad.EmptyUndoBuffer();
	ctrlPad.SetSel(0, 0);
	ctrlPad.SetModify(FALSE);

	ctrlPad.InvalidateRect(NULL);
	ctrlDebug.InvalidateRect(NULL);
	
	m_hMenu = WinUtil::mainMenu;

	bHandled = FALSE;
	return 1;
}

LRESULT ScriptEditorFrame::onClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) {
	if(ctrlPad.GetModify()) {
		if(MessageBox(CTSTRING(SCRIPTEDITOR_CHANGED), (STRING(SCRIPTEDITOR_SAVE) + "?").c_str(), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES) {
			string target = SETTING(LAST_SCRIPT);
			AutoArray<TCHAR> buf(ctrlPad.GetWindowTextLength() + 1);
			ctrlPad.GetWindowText(buf, ctrlPad.GetWindowTextLength() + 1);
			try {
				File(target, File::WRITE, File::CREATE | File::TRUNCATE).write(buf, ctrlPad.GetWindowTextLength());
			} catch(const FileException& e) {
				string msg = STRING(SCRIPTEDITOR_COULD_NOT_SAVE) + "\r\n" + e.getError();
				MessageBox(msg.c_str(), CSTRING(ERRORS), MB_ICONSTOP);
			}
		}
	}
	bHandled = FALSE;
	return 0;
}

void ScriptEditorFrame::UpdateLayout(BOOL bResizeBars /* = TRUE */) {
	RECT rect = { 0 };
	GetClientRect(&rect);

	// position bars and offset their dimensions
	UpdateBarsPosition(rect, bResizeBars);

	setStatus();
	if(ctrlScripts.IsWindow()) {
		CRect rc;
		ctrlScripts.GetClientRect(rc);
		ctrlScripts.SetColumnWidth(0, rc.Width());
	}

	// resize client window
	if(m_hWndClient != NULL)
		::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
			rect.right - rect.left, rect.bottom - rect.top,
			SWP_NOZORDER | SWP_NOACTIVATE);
}

LRESULT ScriptEditorFrame::onCtlColor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	HWND hWnd = (HWND)lParam;
	if(hWnd == ctrlDebug.m_hWnd) {
		HDC hDC = (HDC)wParam;
		::SetBkColor(hDC, bgColor);
		::SetTextColor(hDC, textColor);
		return (LRESULT)bgBrush;
	}
	bHandled = FALSE;
	return FALSE;
}

LRESULT ScriptEditorFrame::onContextMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) {
	// client area of window
	RECT rc;
	// location of mouse click
	POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };

	ctrlScripts.GetClientRect(&rc);
	ctrlScripts.ScreenToClient(&pt); 

	if (PtInRect(&rc, pt)) {
		ctrlScripts.ClientToScreen(&pt);
		CMenu menu;
		menu.CreatePopupMenu();
		if(ctrlScripts.GetSelectedCount() > 0) {
			menu.AppendMenu(MF_STRING, IDC_OPEN_SCRIPT, CTSTRING(OPEN));
			menu.AppendMenu(MF_STRING, IDC_RELOAD_SCRIPT, CTSTRING(SCRIPTEDITOR_RELOAD));
		}
		menu.AppendMenu(MF_STRING, IDC_NEW_SCRIPT, CTSTRING(NEW));
		menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, m_hWnd);
		return TRUE;
	} else {
		ctrlPad.GetWindowRect(&rc);
		GetCursorPos(&pt);
		if (PtInRect(&rc, pt)) {
			CMenu menu;
			menu.CreatePopupMenu();
			menu.AppendMenu(MF_STRING, ID_EDIT_COPY, CTSTRING(COPY));
			menu.AppendMenu(MF_SEPARATOR);
			menu.AppendMenu(MF_STRING, IDC_SAVE_SCRIPT, CTSTRING(SCRIPTEDITOR_SAVE));
			menu.AppendMenu(MF_STRING, IDC_RELOAD_PAD, CTSTRING(SCRIPTEDITOR_RELOAD));
			menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, m_hWnd);
			return TRUE;
		}
	}
	bHandled = FALSE;
	return FALSE;
}

void ScriptEditorFrame::setStatus() {
	if(ctrlStatus.IsWindow()) {
		CRect sr;
		int w[4];
		ctrlStatus.GetClientRect(sr);

		int tmp = (sr.Width() / 4) - 4;
		
		w[0] = tmp;
		w[1] = w[0] + tmp;
		w[2] = w[1] + tmp;
		w[3] = w[2] + tmp;
		
		ctrlStatus.SetParts(4, w);
		ctrlStatus.SetText(0, currentFileName.c_str());
		setLineCount();
	}
}

void ScriptEditorFrame::setLineCount() {
	if(ctrlStatus.IsWindow()) {
		TCHAR buf[64];
		CHARRANGE pos;
		ctrlPad.GetSel(pos);
		_stprintf(buf, _T("Ln %d/%d"), ctrlPad.LineFromChar(pos.cpMin)+1, ctrlPad.GetLineCount());
		ctrlStatus.SetText(1, buf);
	}
}

LRESULT ScriptEditorFrame::onOpen(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	TCHAR buf[BUFLEN];
	ctrlScripts.GetItemText(ctrlScripts.GetSelectedIndex(), 0, buf, BUFLEN);
	tstring target(buf), text;
	target = Util::getAppPath() + "scripts\\" + target;
	try {
		text = File(target, File::READ, File::OPEN).read();
		SettingsManager::getInstance()->set(SettingsManager::LAST_SCRIPT, target);
	} catch(const FileException& e) {
		string msg = STRING(SCRIPTEDITOR_COULD_NOT_LOAD) + "\r\n" + e.getError();
		MessageBox(msg.c_str(), CTSTRING(ERRORS), MB_ICONSTOP);
	}
	ctrlPad.SetWindowText(text.c_str());
	ctrlPad.EmptyUndoBuffer();
	ctrlPad.SetModify(FALSE);
	string::size_type i = target.rfind('\\');
	if(i != string::npos) {
		currentFileName = target.substr(i+1);
		setStatus();
	}
	return 0;
}

LRESULT ScriptEditorFrame::onSave(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	save();
	return 0;
}

LRESULT ScriptEditorFrame::onReloadScript(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	TCHAR buf[BUFLEN];
	ctrlScripts.GetItemText(ctrlScripts.GetSelectedIndex(), 0, buf, BUFLEN);
	if(buf == currentFileName && ctrlPad.GetModify()) {
		if(MessageBox(CTSTRING(SCRIPTEDITOR_CHANGED), (STRING(SCRIPTEDITOR_SAVE) + "?").c_str(), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES) {
			save();
		}
	}
	ScriptManager::getInstance()->EvaluateFile(buf);
	return 0;
}

LRESULT ScriptEditorFrame::onReloadPad(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	if(ctrlPad.GetModify()) {
		if(MessageBox(CTSTRING(SCRIPTEDITOR_CHANGED2), (STRING(SCRIPTEDITOR_SAVE) + "?").c_str(), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES) {
			save();
		}
	}
	ScriptManager::getInstance()->EvaluateFile(currentFileName);
	return 0;
}

LRESULT ScriptEditorFrame::onNewScript(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
	if(ctrlPad.GetModify()) {
		if(MessageBox(CTSTRING(SCRIPTEDITOR_CHANGED3), (STRING(SCRIPTEDITOR_SAVE) +"?").c_str(), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES) {
			save();
		}
	}
	LineDlg dlg;
	dlg.title = STRING(SCRIPTEDITOR_NEW_SCRIPT);
	dlg.description = STRING(SCRIPTEDITOR_ENTER_FILENAME);
	if(dlg.DoModal() == IDOK && !dlg.line.empty()) {
		if(dlg.line.find('.') == string::npos) {
			dlg.line += ".lua";
		}
		string target = Util::getAppPath() + "scripts\\" + dlg.line;
		try {
			File(target, File::WRITE, File::CREATE);
			ctrlPad.SetWindowText(_T(""));
			ctrlPad.SetModify(FALSE);
			SettingsManager::getInstance()->set(SettingsManager::LAST_SCRIPT, target);
			currentFileName = dlg.line;
			ctrlScripts.insert(ctrlScripts.GetItemCount(), currentFileName.c_str());
			setStatus();
		} catch(const FileException& e) {
			string msg = STRING(SCRIPTEDITOR_FAILD_NEWSCRIPT) + "\r\n" + e.getError();
			MessageBox(msg.c_str(), CTSTRING(ERRORS), MB_ICONSTOP);
		}
	}
	return 0;
}

void ScriptEditorFrame::save() {
	string target = SETTING(LAST_SCRIPT);
	if(!target.empty()) {
		AutoArray<TCHAR> buf(ctrlPad.GetWindowTextLength() + 1);
		ctrlPad.GetWindowText(buf, ctrlPad.GetWindowTextLength() + 1);
		try {
			File(target, File::WRITE, File::CREATE | File::TRUNCATE).write(buf, ctrlPad.GetWindowTextLength());
			ctrlPad.SetModify(FALSE);
			string::size_type i = target.rfind('\\');
			if(i != string::npos) {
				target = target.substr(i+1);
				addLine(" ** " + STRING(SCRIPTEDITOR_SAVED) + " " + target + " **");
			}
		} catch(const FileException& e) {
			string msg = STRING(SCRIPTEDITOR_COULD_NOT_SAVE) + "\r\n" + e.getError();
			MessageBox(msg.c_str(), CTSTRING(ERRORS), MB_ICONSTOP);
		}
	}
}

void ScriptEditorFrame::addLine(const string& aLine) {
	ctrlDebug.AppendText(string(aLine + "\r\n").c_str());
	if(ctrlDebug.GetWindowTextLength() > MAX_TEXT_LEN) {
		int trimChars = ctrlDebug.LineIndex(ctrlDebug.LineFromChar(2000));
		ctrlDebug.SetRedraw(FALSE);
		ctrlDebug.SetSel(0, trimChars);
		ctrlDebug.ReplaceSel(_T(""));
		ctrlDebug.SetRedraw(TRUE);
	}
	ctrlDebug.InvalidateRect(NULL);
}